package cn.buk.tms.eotms.dao;

import cn.buk.common.sc.CommonSearchCriteria;
import cn.buk.common.sc.Page;
import cn.buk.common.util.DateUtil;
import cn.buk.tms.common.constant.TmsOrderConstant;
import cn.buk.tms.eotms.entity.*;
import cn.buk.tms.eotms.sc.SearchFlightOrderCriteria;
import org.springframework.stereotype.Component;

import jakarta.persistence.criteria.*;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static cn.buk.tms.common.constant.TmsOrderConstant.*;

@Component
@Transactional
public class AttractionDaoImpl extends AbstractDao implements AttractionDao {

  @Override
  public AttractionInfo getAttractionInfoById(int id) {
    return em.find(AttractionInfo.class, id);
  }

  @Override
  public AttractionInfo getAttractionInfoByName(final int ownerId, String attractionName) {
    List<AttractionInfo> list = em.createQuery("select o from AttractionInfo o " +
            "where o.enterpriseId = :ownerId " +
            "and o.name = :name", AttractionInfo.class)
            .setParameter("ownerId", ownerId)
            .setParameter("name", attractionName)
            .getResultList();

    return list.size() == 1 ? list.get(0): null;
  }

  @Override
  public List<AttractionInfo> searchAttractions(final int userId, CommonSearchCriteria sc) {
    User user = em.find(User.class, userId);
    final int ownerId = user.getParentEnterprise();

    List<AttractionInfo> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<AttractionInfo> cq = cb.createQuery(AttractionInfo.class);
    Root<AttractionInfo> root = cq.from(AttractionInfo.class);
    root.alias("o");

    var predicates0 = getPredicates_searchAttractions(cb, root, ownerId);

    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<AttractionInfo> countRoot = countQuery.from(AttractionInfo.class);
    countRoot.alias("o");

    var predicates1 = getPredicates_searchAttractions(cb, countRoot, ownerId);

    countQuery.select(cb.count(countRoot)).where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    if (count.intValue() > 0) {
      cq.where(predicates0.toArray(new Predicate[predicates0.size()]));
//      List<jakarta.persistence.criteria.Order> orderByes = new ArrayList<>();


      Page page = sc.getPage();
      page.setRowCount(count.intValue());
      int i = (page.getPageNo() - 1) * page.getPageSize();

      results = em.createQuery(cq)
              .setFirstResult(i)
              .setMaxResults(page.getPageSize()).getResultList();
    }

    return results == null ? new ArrayList<>() : results;
  }

  private static List<Predicate> getPredicates_searchAttractions(CriteriaBuilder cb, Root<AttractionInfo> root, int ownerId) {
    List<Predicate> predicates = new ArrayList<>();
    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(AttractionInfo_.enterpriseId), ownerId);
    predicates.add(predicate);
    return predicates;
  }

  @Override
  public List<AttractionPrice> searchAttractionPrices(int userId, int attractionId, CommonSearchCriteria sc) {
    User user = em.find(User.class, userId);
    final int ownerId = user.getParentEnterprise();

    List<AttractionPrice> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<AttractionPrice> cq = cb.createQuery(AttractionPrice.class);
    Root<AttractionPrice> root = cq.from(AttractionPrice.class);
    root.alias("o");

    List<Predicate> predicates = new ArrayList<>();
    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(AttractionPrice_.enterpriseId), ownerId);
    predicates.add(predicate);

    predicate = cb.equal(root.get(AttractionPrice_.attractionInfo), attractionId);
    predicates.add(predicate);


    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<AttractionPrice> countRoot = countQuery.from(AttractionPrice.class);
    countRoot.alias("o");

    countQuery.select(cb.count(countRoot)).where(predicates.toArray(new Predicate[predicates.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    if (count.intValue() > 0) {
      cq.where(predicates.toArray(new Predicate[predicates.size()]));
      List<jakarta.persistence.criteria.Order> orderByes = new ArrayList<>();
      orderByes.add(cb.asc(root.get("effectDate")));
      cq.orderBy(orderByes);

      Page page = sc.getPage();
      page.setRowCount(count.intValue());
      int i = (page.getPageNo() - 1) * page.getPageSize();

      results = em.createQuery(cq)
              .setFirstResult(i)
              .setMaxResults(page.getPageSize()).getResultList();
    }

    return results == null ? new ArrayList<>() : results;
  }

  @Override
  public AttractionPrice searchAttractionPriceByDate(int ownerId, int attractionId, Date travelDate) {
    List<AttractionPrice> prices = em.createQuery("select o from AttractionPrice o " +
            "where o.enterpriseId = :ownerId " +
            "and o.attractionInfo.id = :attractionId " +
            "and o.effectDate = :travelDate", AttractionPrice.class)
            .setParameter("ownerId", ownerId)
            .setParameter("attractionId", attractionId)
            .setParameter("travelDate", travelDate)
            .getResultList();
    return prices.size() == 1 ? prices.get(0): null;
  }

  @Override
  public int createTicketOrder(final int userId, TicketOrder ticketOrder) {
    User user = em.find(User.class, userId);
    final int ownerId = user.getParentEnterprise();
    em.persist(ticketOrder);

    OperationLog log = OperationLog.createLog(ownerId, TmsOrderConstant.OPERATING_OBJECT_TICKET_ORDER);
    log.setOperator(user.getUsername());
    log.setContent("创建订单");
    log.setOrderId(ticketOrder.getId());
    log.setCustomerId(ticketOrder.getCustomer().getId());
    em.persist(log);

    return ticketOrder.getId();
  }

  @Override
  public List<TicketOrder> searchTicketOrders(final int ownerId, final int customerId, SearchFlightOrderCriteria sc) {
    List<TicketOrder> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<TicketOrder> cq = cb.createQuery(TicketOrder.class);
    Root<TicketOrder> root = cq.from(TicketOrder.class);
    root.alias("o");

    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(TicketOrder_.enterpriseId), ownerId);
    predicates.add(predicate);


    predicate = cb.equal(root.get(TicketOrder_.customer), customerId);
    predicates.add(predicate);

    if (sc.getOrderNo().length() > 0) {
      predicate = cb.equal(root.get(TicketOrder_.orderNo), sc.getOrderNo());
      predicates.add(predicate);
    }

    if (sc.getStatus() > -1) {
      predicate = cb.equal(root.get(TicketOrder_.status), sc.getStatus());
      predicates.add(predicate);
    } else if (sc.getOrderNo().length() == 0) {
      //默认不显示已取消订单
      predicate = cb.notEqual(root.get(TicketOrder_.status), TICKET_ORDER_STATUS_CANCELED);
      predicates.add(predicate);
    }


    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<TicketOrder> countRoot = countQuery.from(TicketOrder.class);
    countRoot.alias("o");

    countQuery.select(cb.count(countRoot))
            .where(predicates.toArray(new Predicate[predicates.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    int maxResults = count.intValue();
    if (maxResults > 0) {
      cq.where(predicates.toArray(new Predicate[predicates.size()]));
      List<jakarta.persistence.criteria.Order> orderByes = new ArrayList<>();
      orderByes.add(cb.desc(root.get("id")));

      cq.orderBy(orderByes);

      Page page = sc.getPage();
      page.setRowCount(maxResults);
      int i = (page.getPageNo() - 1) * page.getPageSize();

      if (i < 0) {
        i = 0;
      }

      results = em.createQuery(cq)
              .setFirstResult(i)
              .setMaxResults(page.getPageSize()).getResultList();
    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  @Override
  public TicketOrder getTicketOrderById(int orderId) {
    return em.find(TicketOrder.class, orderId);
  }

  @Override
  public int cancelTicketOrder(int ownerId, int customerId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update TicketOrder o set " +
            "o.status = :newStatus, o.lastUpdate = :lastUpdate " +
            "where o.id = :id " +
            "and o.enterpriseId = :ownerId " +
            "and o.customer.id = :customerId " +
            "and o.status = :oldStatus ")
            .setParameter("id", orderId)
            .setParameter("customerId", customerId)
            .setParameter("ownerId", ownerId)
            .setParameter("oldStatus", TICKET_ORDER_STATUS_NOT_SUBMITTED)
            .setParameter("newStatus", TICKET_ORDER_STATUS_CANCELED)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("To cancel ticket order failed.");
    }

    OperationLog log = OperationLog.createLog(ownerId, OPERATING_OBJECT_TICKET_ORDER);
    log.setOperator(username);
    log.setOrderId(orderId);
    log.setContent("取消订单: " + remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int payTicketOrder(final int ownerId, int customerEnterpriseId, final int customerId, int userId, int orderId) {
    int retCode = 0;
    final TicketOrder ticketOrder = em.find(TicketOrder.class, orderId);
    final User user = em.find(User.class, userId);
    if (ticketOrder.getEnterpriseId() != ownerId
            || ticketOrder.getCustomer().getId() != customerId) {
      throw new RuntimeException("Ticket order is not existed.");
    }

    EnterpriseBalance entBalance = doGetEnterpriseBalance(ownerId, customerEnterpriseId);

    final int fee = (int)(ticketOrder.getTotal() * 100);

    if (entBalance.getCashBalance() < fee) {
      retCode = -1;
      //日志
      throw new RuntimeException("现金账户余额不足(Cash balance is not enough).");
    } else {
      AmountHistory amountHistory = new AmountHistory();
      amountHistory.setOwnerId(ownerId);
      amountHistory.setEnterpriseId(customerEnterpriseId);
      amountHistory.setUserId(userId);
      amountHistory.setOrderType(ORDER_TYPE_TICKET);
      amountHistory.setOrderId(orderId);
      amountHistory.setDebt(fee);
      amountHistory.setRemark("为门票订单(" + ticketOrder.getOrderNo() + ")付款。");

      entBalance.decCashBalance(fee);
      amountHistory.setAccountBalance(entBalance.getCashBalance());

      em.merge(entBalance);
      em.persist(amountHistory);

      //门票订单
      ticketOrder.setPayStatus(PAY_STATUS_PAID);
      ticketOrder.setStatus(TICKET_ORDER_STATUS_WAITING);
      ticketOrder.setLastUpdate(DateUtil.getCurDateTime());
      em.merge(ticketOrder);

      //日志
      OperationLog log = OperationLog.createLog(ownerId, OPERATING_OBJECT_TICKET_ORDER);
      log.setOperator(user.getUsername());
      log.setContent("订单支付成功.");
      log.setOrderId(orderId);
      em.persist(log);


      retCode = 100;
    }

    return retCode;
  }

}
